package app

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"shop-common/library/variables"
	"syscall"
	"time"
)

var _ Hook = (*hook)(nil)

// Hook a graceful shutdown hook, default with signals of SIGINT and SIGTERM
type Hook interface {
	// WithSignals add more signals into hook
	WithSignals(...syscall.Signal) Hook

	// Close register shutdown handles
	Close(...func())
}

type hook struct {
	ctx chan os.Signal
}

// NewHook create a Hook instance
func NewHook() Hook {
	hook := &hook{
		ctx: make(chan os.Signal, 1),
	}

	return hook.WithSignals(syscall.SIGINT, syscall.SIGTERM)
}

func (h *hook) WithSignals(signals ...syscall.Signal) Hook {
	for _, s := range signals {
		signal.Notify(h.ctx, s)
	}

	return h
}

func (h *hook) Close(fns ...func()) {
	select {
	case <-h.ctx:
	}
	signal.Stop(h.ctx)

	for _, fn := range fns {
		fn()
	}
}

func beforeDestroy(server *http.Server) {
	NewHook().Close(
		// 关闭 db
		func() {
			db, _ := variables.GormDB().DB()
			if err := db.Close(); err != nil {
				log.Printf("db close err %v\n", err)
			} else {
				log.Println("db close success")
			}
		},
		// 关闭 redis
		func() {
			if err := variables.RedisConn.Close(); err != nil {
				log.Printf("redis close err %v\n", err)
			} else {
				log.Println("redis close success")
			}
		},
		// 关闭 http server
		func() {
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
			defer cancel()

			if err := server.Shutdown(ctx); err != nil {
				log.Printf("server shutdown err %v\n", err)
			} else {
				log.Println("server shutdown success")
			}
		},
	)
}
